//
//  ZZRACTests.m
//  ZZRACTests
//
//  Created by 赵梁 on 2018/10/31.
//  Copyright © 2018 layzhao. All rights reserved.
//

#import <XCTest/XCTest.h>

#import <ReactiveObjC.h>
#import "Person.h"



@interface ZZRACTests : XCTestCase

@end

@implementation ZZRACTests

- (void)setUp {
    // Put setup code here. This method is called before the invocation of each test method in the class.
}

- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}

- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

- (void)testPerformanceExample {
    // This is an example of a performance test case.
    [self measureBlock:^{
        // Put the code you want to measure the time of here.
    }];
}


// RACSignal使用步骤：
// 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe
// 2.订阅信号,才会激活信号. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 3.发送信号 - (void)sendNext:(id)value
- (void)testsignal
{
    //三步骤:创建信号,订阅信号,发送信号
    
    //1.创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        //保存起来 就不会被取消订阅
//        _subscriber = subscriber;
        //3.发送信号
        [subscriber sendNext:@1];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"默认信号发送完毕被取消");
        }];
    }];
    
    //如果要取消就拿到 RACDisposable
    
    //2.订阅信号
    RACDisposable *disposable = [signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    //取消订阅
    [disposable dispose];
}

// RACSubject使用步骤

// 1.创建信号 [RACSubject subject]，跟RACSiganl不一样，创建信号时没有block。
// 2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 3.发送信号 sendNext:(id)value

- (void)testRACSubject
{
    //创建信号
    RACSubject *subject = [RACSubject subject];
    //订阅信号
    [subject subscribeNext:^(id x) {
        NSLog(@"订阅者一%@",x);
    }];
    
    [subject subscribeNext:^(id x) {
        NSLog(@"订阅者二%@",x);
    }];
    
    //发送信号
    [subject sendNext:@"111"];
}

// RACReplaySubject使用步骤:
// 1.创建信号 [RACSubject subject]，跟RACSiganl不一样，创建信号时没有block。
// 2.可以先订阅信号，也可以先发送信号。
// 2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 2.2 发送信号 sendNext:(id)value
- (void)testRACReplaySubject
{
    //创建信号
    RACReplaySubject *replaySubject = [RACReplaySubject subject];

    //发送信号
    [replaySubject sendNext:@"222"];
    [replaySubject sendNext:@"333"];

    //订阅信号
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"第一个订阅者%@",x);
    }];
    
    //如果想一个信号被订阅，就重复播放之前所有值，需要先发送信号，在订阅信号。
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"第二个订阅者%@",x);
    }];
    
    [replaySubject sendNext:@"444"];
}

#pragma mark -- 监听事件(按钮点击)
- (void)testTouchEvent
{
    //原理：将系统的UIControlEventTouchUpInside事件转化为信号、我们只需要订阅该信号就可以了。
    //点击按钮的时候触发UIControlEventTouchUpInside事件---> 发出信号 实际是:  执行订阅者(subscriber)的sendNext方法
    
    // 外界使用
//    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
//        //x 就是被点击的按钮
//        NSLog(@"按钮被点击了%@", x);
//    }];
}

#pragma mark -- 代替代理
//需求：自定义redView,监听红色view中按钮点击
//之前都是需要通过代理监听，给红色View添加一个代理属性，点击按钮的时候，通知代理做事情,符合封装的思想。
//rac_signalForSelector:把调用某个对象的方法的信息转换成信号(RACSubject)，就会调用这个方法，就会发送信号。
//这里表示只要监听了redView的btnClick:方法。(只要redView的btnClick:方法执行了,就会执行下面的方法,并且将参数传递过来)

- (void)testDelegate
{
//    [[redView rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
//        　　NSLog(@"点击红色视图中的按钮", x);
//    }];
}

#pragma mark KVO
- (void)testKVO2
{
    Person *person = [Person new];
    person.age = 6;
    [[person rac_valuesAndChangesForKeyPath:@"age" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
    person.age = 7;
    person.age = 8;
}

#pragma mark -- 代替通知
// 把监听到的通知转换信号
- (void)testNotification
{
    [[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] takeUntil:[self rac_willDeallocSignal]] subscribeNext:^(NSNotification * _Nullable x) {
        NSLog(@"%@", x);
    }];
}

#pragma mark -- 监听文本框的文字改变
// 监听文本框的文字改变、获取文本框文字改变的信号
- (void)testUIView
{
//    [_textField.rac_textSignal subscribeNext:^(id x) {
//        self.textLabel.text = x;
//        NSLog(@"文字改变了%@",x);
//    }];
}
#pragma mark -- 处理多个请求

- (void)testMutiRequests
{
    // 处理多个请求，都返回结果的时候，统一做处理.
    RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        // 发送请求1
        [subscriber sendNext:@"发送请求1"];
        return nil;
    }];
    
    RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 发送请求2
        [subscriber sendNext:@"发送请求2"];
        return nil;
    }];
    [request2 subscribeNext:^(id  _Nullable x) {
        NSLog(@"x : %@",x);
    }];
    
    // 使用注意：几个信号，selector的方法就几个参数，每个参数对应信号发出的数据。
//    不需要订阅:不需要主动订阅,内部会主动订阅
    [self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1, request2]];
}

// 更新UI
- (void)updateUIWithR1:(id)data r2:(id)data1
{
    NSLog(@"更新UI%@ %@",data, data1);
}

//RACTuple:元组类,类似NSArray,用来包装值.
//RACSequence:RAC中的集合类，用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
//使用场景：1.字典转模型

#pragma mark 遍历数组
// 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
// 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
// 第三步: 订阅信号，激活信号，会自动把集合中的所有值，遍历出来。
- (void)testRACArrayTuple
{
    NSArray *array = @[@1,@2,@3];
    [array.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}

#pragma mark 字典Tuple
- (void)testRACdictTuple
{
    NSDictionary *dict = @{@"name":@"张三",@"age":@"20",@"sex":@"男"};
    [dict.rac_sequence.signal subscribeNext:^(id x) {
        
        // 解包元组，会把元组的值，按顺序给参数里面的变量赋值
        // RACTupleUnpack这是个宏,后面会介绍
        RACTupleUnpack(NSString *key, NSString *value) = x;

        NSLog(@"%@---%@",key,value);
//        相当于
//        NSString *key = x[0];
//        NSString *value = x[1];
    }];
}

#pragma mark 字典转模型
- (void)testRACdicToModel
{
    //  RAC高级写法:
    NSArray *array = @[@{@"index":@1},@{@"index":@2},@{@"index":@3}];
    // map:映射的意思，目的：把原始值value映射成一个新值
    // array: 把集合转换成数组
    // 底层实现：当信号被订阅，会遍历集合中的原始值，映射成新值，并且保存到新的数组里。
    
    NSArray *musicArray =[[array.rac_sequence map:^id(id value) {
        return value[@"index"];
    }] array];
    NSLog(@"--------%@",musicArray);
}

#pragma mark ***********************************************RAC常用宏*****************************************
#pragma mark KVO
//RACObserve(就是一个宏定义):快速的监听某个对象的某个属性改变
//监听person的age属性,当age发生改变的时候就会触发NSLog方法
- (void)testKVO
{
    Person *person = [Person new];
    person.age = 6;
    [RACObserve(person, age) subscribeNext:^(id x) {
        NSLog(@"age %@", x);
    }];
    person.age = 7;
    person.age = 8;
}

#pragma mark 登录按钮的状态实时监听
- (void)testObserveMutiValueChange
{
    Person *person = [Person new];
    person.age = 6;
    
    RACSignal *single  = RACObserve(person, age);
    RACSignal *single2  = RACObserve(person, name);
    
    Person *person2 = [Person new];
    
    [RACObserve(person, name) subscribeNext:^(id x) {
        NSLog(@"name %@", x);
    }];
    
    RAC(person2, age) = [RACSignal combineLatest:@[single, single2] reduce:^id _Nullable(NSNumber *age, NSString * name) {
        NSLog(@"name %@,  age  %@", name, age);
        if ([name isEqualToString:@"job"]) {
            return @(2);
        }
        return @(10);
    }];
    
    person.name = @"steve";
    person.name = @"job";
}

#pragma mark--循环引用
- (void)testBlockLinkSelf
{
    // @weakify() 宏定义
    @weakify(self) //相当于__weak typeof(self) weakSelf = self;
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        @strongify(self)  //相当于__strong typeof(weakSelf) strongSelf = weakSelf;
        NSLog(@"%@",subscriber);
        return nil;
    }];
}

@end
